package com.zonski.jbots.engine;

import com.zonski.jbots.engine.entity.MovingEntity;

import java.util.Vector;

/**
 * This class encapsulates the state of a room in ways that are nice
 * for artificially intelligent objects to understand
 */
public class RoomStateMatrix 
{
    private static Rectangle C_RECT = new Rectangle(0, 0, 0, 0);

    private Room room;
    private QuickVector[][] tileEntities;
    private QuickVector external;
    private boolean factorVelocity;
    private Rectangle allBounds;

    public RoomStateMatrix(boolean factorVelocity)
    {
        this.factorVelocity = factorVelocity;
        this.external = new QuickVector(10);
    }

    public final Room getRoom()
    {
        return this.room;
    }

    public final void setRoom(Room room)
    {
        if(room == null)
        {
            this.tileEntities = null;
            this.allBounds = null;
        }
        else if(this.room == null || this.room.getNumberOfTilesDown() != room.getNumberOfTilesDown() ||
                this.room.getNumberOfTilesAcross() != room.getNumberOfTilesAcross())
        {
            this.tileEntities = new QuickVector[room.getNumberOfTilesDown()][room.getNumberOfTilesAcross()];
            this.allBounds = new Rectangle(0, 0, room.getNumberOfTilesAcross(), room.getNumberOfTilesDown());
        }
        this.room = room;
    }

    /**
     * Loads the matrix from the room
     */
    public final void update()
    {
        this.external.removeAllElements();
        for(int i=0; i<this.tileEntities.length; i++)
        {
            for(int j=0; j<this.tileEntities[i].length; j++)
            {
                if(this.tileEntities[i][j] != null)
                {
                    this.tileEntities[i][j].removeAllElements();
                }
            }
        }
        Rectangle tileBounds;
        QuickVector layers = this.room.getLayers();
        for(int i=0; i<layers.size(); i++)
        {
            Layer layer = (Layer)layers.elementAt(i);
            QuickVector entities = layer.getEntities();
            for(int j=0; j<entities.size(); j++)
            {
                Entity entity = (Entity)entities.elementAt(j);
                addEntity(entity);
            }
        }
    }

    /** 
     * Obtains all the entities on the specified tile
     */
    public final QuickVector getEntitiesOnTile(int x, int y)
    {
        QuickVector entities;
        if(this.allBounds.contains(x, y))
        {
            entities = this.tileEntities[y][x];
        }else{
            entities = this.external;
        }
        return entities;
    }

    /**
     * Obtains the rectangle in terms of tile bounds that the 
     * bounds passed in overlap, this can then be iterated through
     */
    public final Rectangle convertToTileBounds(Rectangle bounds, Rectangle use)
    {
        int tileSize = this.room.getTileSize();
        int x = bounds.x;
        int y = bounds.y;
        int width;
        int height;
        int ux;
        if(x >= 0 || x%tileSize == 0)
        {
            ux = x / tileSize;
        }else{
            ux = (x / tileSize) - 1;
        }

        int uy;
        if(y >=0 || y%tileSize == 0)
        {
            uy = y / tileSize;
        }else{
            uy = (y / tileSize) - 1;
        }
        width = bounds.width;
        height = bounds.height;

        int uw;
        if(width > 0)
        {
            int add;
            if((bounds.rightEdge) % tileSize > 0)
            {
                add = 1;
            }else{
                add = 0;
            }
            uw = (bounds.rightEdge) / tileSize + add - ux;
        }else{
            uw = 0;
        }
        int uh;
        if(height > 0 )
        {
            int add;
            if((bounds.bottomEdge) % tileSize > 0)
            {
                add = 1;
            }else{
                add = 0;
            }
            uh = (bounds.bottomEdge) / tileSize + add - uy;
        }else{
            uh = 0;
        }

        use.setLocation(ux, uy, uw, uh);
        return use;
    }
    /*
    public final Rectangle convertToTileBounds(Rectangle bounds, Rectangle use)
    {
        int tileSize = this.room.getTileSize();
        int x = bounds.x;
        int y = bounds.y;
        int roomWidth = room.getNumberOfTilesAcross();
        int roomHeight = room.getNumberOfTilesDown();
        int width;
        int height;
        int ux;
        if(x < 0)
        {
            ux = 0;
        }else{
            ux = x / tileSize;
        }
        int uy;
        if(y < 0)
        {
            uy = 0;
        }else{
            uy = y / tileSize;
        }

        if(x < 0)
        {
            width = Math.max(0, bounds.width + x);
            x = 0;
        }else{
            width = bounds.width;
        }
        if(y < 0)
        {
            height = Math.max(0, bounds.height + y);
            y = 0;
        }else{
            height = bounds.height;
        }
        int uw;
        if(width > 0)
        {
            int add;
            if((x+width) % tileSize > 0)
            {
                add = 1;
            }else{
                add = 0;
            }
            uw = Math.min(
                    ((x+width) / tileSize + add),
                    roomWidth) - ux;
        }else{
            uw = 0;
        }
        int uh;
        if(height > 0 )
        {
            int add;
            if((y + height) % tileSize > 0)
            {
                add = 1;
            }else{
                add = 0;
            }
            uh = Math.min(
                    (y+height) / tileSize + add,
                    roomHeight) - uy;
        }else{
            uh = 0;
        }

        use.setLocation(ux, uy, uw, uh);
        return use;
    }
    */

    public void addEntity(Entity entity)
    {
        Rectangle tileBounds;
        Rectangle bounds;
        if(this.factorVelocity)
        {
            bounds = entity.bounds;
            Rectangle.expand(bounds, entity.xVelocity, entity.yVelocity, C_RECT);
            bounds = C_RECT;
        }else{
            bounds = entity.bounds;
        }
        if(entity instanceof MovingEntity)
        {
            MovingEntity me = (MovingEntity)entity;
            if(me.oldTileBounds == null)
            {
                me.oldTileBounds = new Rectangle(0, 0, 0, 0);
            }
            tileBounds = convertToTileBounds(bounds, me.oldTileBounds);
        }else{
            tileBounds = convertToTileBounds(bounds, C_RECT);
        }
        addEntity(entity, tileBounds);
    }

    public void addEntity(Entity entity, Rectangle tileBounds)
    {
        /*
        if(tileBounds.width * tileBounds.height > 2)
        {
            System.out.println(entity.entityType.getName()+"("+entity.layerId+") covers "+
                    (tileBounds.width * tileBounds.height));
        }
        */

        QuickVector ext = null;
        int maxx = tileBounds.rightEdge;
        if(maxx > this.allBounds.rightEdge)
        {
            maxx = this.allBounds.rightEdge;
            ext = this.external;
        }
        int maxy = tileBounds.bottomEdge;
        if(maxy > this.allBounds.bottomEdge)
        {
            maxy = this.allBounds.bottomEdge;
            ext = this.external;
        }
        int minx = tileBounds.x;
        if(minx < 0)
        {
            minx = 0;
            ext = this.external;
        }
        int miny = tileBounds.y;
        if(miny < 0)
        {
            miny = 0;
            ext = this.external;
        }

        for(int x = minx; x<maxx; x++)
        {
            for(int y = miny; y<maxy; y++)
            {
                QuickVector v = this.tileEntities[y][x];
                if(v == null)
                {
                    v = new QuickVector(4);
                    this.tileEntities[y][x] = v;
                }
                v.addElement(entity);
            }
        }
        if(ext != null)
        {
            ext.addElement(entity);
        }
    }

    public void removeEntity(Entity entity)
    {
        removeEntity(entity, this.allBounds);
        this.external.removeElement(entity);
    }

    public void removeEntity(Entity entity, Rectangle tileBounds)
    {
        QuickVector ext = null;
        int maxx = tileBounds.rightEdge;
        if(maxx > this.allBounds.rightEdge)
        {
            maxx = this.allBounds.rightEdge;
            ext = this.external;
        }
        int maxy = tileBounds.bottomEdge;
        if(maxy > this.allBounds.bottomEdge)
        {
            maxy = this.allBounds.bottomEdge;
            ext = this.external;
        }
        int minx = tileBounds.x;
        if(minx < 0)
        {
            minx = 0;
            ext = this.external;
        }
        int miny = tileBounds.y;
        if(miny < 0)
        {
            miny = 0;
            ext = this.external;
        }

        for(int x=minx; x<maxx; x++)
        {
            for(int y=miny; y<maxy; y++)
            {
                QuickVector entities = this.tileEntities[y][x];
                if(entities != null)
                {
                    entities.removeElement(entity);
                }
            }
        }
        if(ext != null)
        {
            ext.removeElement(entity);
        }
    }
}
